#!/usr/bin/env python2
#-*- coding: utf-8 -*-

import traceback


from Ump.common import log
from Ump.common import exception
from Ump.objs.db import models
from Ump.objs.session_wrapper import op_logger
from Ump.objs.session_wrapper import enable_log_and_session, _sw
from Ump.objs.manager_base import Manager
from Ump.lich.snapshot import LichSnapshot, LichSnapshotParam


LOG = log.get_log('Ump.objs.snapshot.manager')


SNAP_STATUS_NORMAL = ''
SNAP_STATUS_PROTECTED = 'protected'


@models.add_model(models.Snapshot)
class SnapshotManager(Manager):

    def __init__(self):
        self.super_ = super(SnapshotManager, self).__init__()
        self.lichSnapshot = LichSnapshot()

    @enable_log_and_session(resource='snapshot', event='create')
    def create(self, _logger, kwargs):
        path = kwargs['path']

        _logger.update_props(oplog_obj=path, user_id=kwargs.get('op_user_id'))

        snapshot = self._create(**kwargs)
        return snapshot
            
    def _create(self, path, description=None, cluster_id=None, **kwargs):
        snapshot = _sw.get_snapshot(path)
        if snapshot:
            raise exception.AlreadyExists(path=path)  

        volume = _sw.get_volume(path)
        if not volume:
            raise exception.VolumeNotFound(path=path)

        values = {
            'user_id': _sw.get_user_id(path.username),
            'volume_id': volume.id,
            'name': path.snap_name,
            'description': description,
        }

        # TODO LICH
        param = LichSnapshotParam(path, cluster_id=cluster_id)
        res = self.lichSnapshot.create(param)

        snapshot = models.Snapshot(values).save()

        return snapshot

    @enable_log_and_session(resource='snapshot', event='delete')
    def delete(self, _logger, kwargs):
        snapshot = _sw.get_snapshot(kwargs)
        if not snapshot:
            raise exception.SnapshotNotFound(kwargs)

        _logger.update_props(oplog_obj=snapshot.path)

        res = self._delete(snapshot)
        return res

    def _delete(self, snapshot):
        self._check_before_delete(snapshot)

        path = snapshot.path
        cluster_id = snapshot.volume.cluster_id

        snapshot = snapshot.delete()

        param = LichSnapshotParam(path, cluster_id=cluster_id)
        res = self.lichSnapshot.delete(param)

        return snapshot

    @enable_log_and_session(resource='snapshot', event='protect')
    def protect(self, _logger, kwargs):
        snapshot = _sw.get_snapshot(kwargs)
        if not snapshot:
            raise exception.SnapshotNotFound(kwargs)

        _logger.update_props(oplog_obj=snapshot.path)

        is_protect = kwargs.get('is_protect', False)

        # DB
        status = SNAP_STATUS_PROTECTED if is_protect else SNAP_STATUS_NORMAL
        snapshot.status = status

        # LICH
        param = LichSnapshotParam(snapshot.path, cluster_id=snapshot.volume.cluster_id)
        self.lichSnapshot.protect(param, on=is_protect)

        return snapshot

    def update(self, kwargs):
        # TODO
        snapshot = self._update(**kwargs)
        return snapshot

    def _update(self, name, cluster_id=None, **kwargs):
        raise NotImplementedError

    @enable_log_and_session(resource='snapshot', event='rollback')
    def rollback(self, _logger, kwargs):
        snapshot = _sw.get_snapshot(kwargs)
        if not snapshot:
            raise exception.SnapshotNotFound(kwargs)

        _logger.update_props(oplog_obj=snapshot.path)

        snapshot = self._rollback(snapshot)
        return snapshot

    def _rollback(self, snapshot):
        self._check_before_rollback(snapshot)

        # LICH
        volume = snapshot.volume
        param = LichSnapshotParam(snapshot.path, cluster_id=volume.cluster_id)
        res = self.lichSnapshot.rollback(param)

        # DB
        try:
            self._sync_snapshot_with_volume(snapshot.volume_id)
        except Exception, e:
            LOG.info(e)
        return snapshot

    @enable_log_and_session(resource='snapshot', event='clone')
    def clone(self, _logger, kwargs):
        snapshot = _sw.get_snapshot(kwargs)
        if not snapshot:
            raise exception.SnapshotNotFound(kwargs)

        path2 = kwargs.get('path2')
        oplog_obj = {
            'path': snapshot.path,
            'path2': path2,
        }

        _logger.update_props(oplog_obj=oplog_obj)

        volume = self._clone(snapshot.volume, snapshot, kwargs)
        return volume

    def _clone(self, volume, snapshot, kwargs):

        path2 = kwargs['path2']
        pool2 = _sw.get_pool(path2)
        if not pool2:
            raise exception.TipException(code=300)

        _sw.check_pool_quota(pool2, volume.size, old_size=None)

        new_volume = _sw.get_volume(path2)
        if new_volume:
            if new_volume.user.name != 'cinder':
                raise exception.AlreadyExists(path2=path2)

        values = {
            'name': path2.vol_name,
            'cluster_id': volume.cluster_id,
            'protection_domain_id': volume.protection_domain_id,
            'protocol': pool2.protocol,
            'user_id': pool2.user_id,
            'pool_id': pool2.id,
            'snapshot_id': snapshot.id,
            'clone_type': 'clone',
            'size': volume.size,
            'used': volume.used,
            'provisioning': volume.provisioning,
            'snapshot_policys': volume.snapshot_policys,
            'priority': volume.priority,
            'access_policy': volume.access_policy,
            'is_share': False,
        }
        volume = models.Volume(values).save()

        param = LichSnapshotParam(snapshot.path)
        res = self.lichSnapshot.clone(param, path2)

        try:
            self._sync_snapshot_with_volume(snapshot.volume_id)
        except Exception, e:
            LOG.info(e)
        return volume

    def _check_before_delete(self, snapshot):
        if snapshot.status == SNAP_STATUS_PROTECTED:
            raise exception.TipException(code=501)

        if snapshot.has_clone_volumes():
            raise exception.SnapshotReferenced(u'快照存在克隆卷')

        return True

    def _check_before_rollback(self, snapshot):
        snapshots = snapshot.volume.snapshots
        snapshots = sorted(snapshots, key=lambda x : x.created_at, reverse=True)

        has_clone_volume = False
        for snap in snapshots:
            if snapshot.id == snap.id:
                break

            has_clone_volume = snap.has_clone_volumes()
            if has_clone_volume:
                break

        if has_clone_volume :
            raise exception.SnapshotReferenced(u"下游快照存在克隆卷，不可回滚")

        return True

    def snapshot_time(self, kwargs):
        volume_id = kwargs['volume_id']
        volume = _sw.db_volume(volume_id)

        with op_logger(event='settask', resource='snapshot', oplog_obj=volume.path):
            self._snapshow_time(**kwargs)

    def _snapshow_time(self, volume, count, unit, **kwargs):
        lunPath = "/%s/%s/%s" % (volume.protocol_root, volume.pool.realname, volume.name)
        policy = ''
        if unit.startswith('hour'):
            policy = "%sh-1" % count
        elif unit.startswith('day'):
            policy = "%sd-1" % count
        elif unit.startswith('minute'):
            policy = "%sM-1" % count
        else:
            raise Exception("定时快照周期的单位错误")
        if not  policy :
            raise Exception("定时快照周期错误")
        cmd = "%s/lich/libexec/lichfs --attrset  %s  snapshot_switch  on" % (self.lich_home, lunPath)
        cmd += " && %s/lich/libexec/lichfs --attrset %s snapshot_reserve %s" % (self.lich_home, lunPath, policy)

        host_ip = self._select_http(volume.cluster_id)
        res = self.api_sync_call(host_ip, cmd)

        volume.update({'snapshot_time': policy})
        return volume

    def sync_snapshot(self, cluster_id=1):
        volumes = _sw.db_volumes({'cluster_id': cluster_id})
        for volume in volumes:
            self.utils.exception_pass(self._sync_snapshot_with_volume, volume)

    def snapshot_manager(self, cluster_id=1):

        host_ip = self.lichSnapshot._select_http(cluster_id)
        cmd = '%s/lich/admin/node.py --snapshot_manage' % self.lich_home
        res = self.api_sync_call(host_ip, cmd)

        self.sync_snapshot()

    def _sync_snapshot_with_volume(self, volume):
        cgsnameshots = volume.vgroup.cgsnapshots if volume.vgroup else []
        cgsnameshot_names = [x.name for x in cgsnameshots]

        # TODO 
        param = LichSnapshotParam(volume.path, cluster_id=volume.cluster_id)
        lich_snapshots = self.lichSnapshot.list(param)

        dbsnapshots = _sw.db_snapshots({'volume_id': volume.id})
        for dbsnapshot in dbsnapshots:
            if dbsnapshot.name in cgsnameshot_names:
                dbsnapshot.delete()

            if dbsnapshot.name in lich_snapshots:
                continue

            dbsnapshot.delete()

        for snapshotName in lich_snapshots:
            snapshot = _sw.db_snapshot({'volume_id': volume.id, 'name': snapshotName})
            if snapshot:
                if not snapshot.user_id:
                    snapshot.update({'user_id':volume.user_id})
                continue

            if snapshotName in cgsnameshot_names:
                continue

            values = {
                'name': snapshotName,
                'volume_id': volume.id,
                'user_id': volume.user_id,
            }

            snapshot = models.Snapshot(values).save()
        return

        
if __name__ == '__main__':
    snm = SnapshotManager()
    snm.sync_snapshot(1)
